using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using graph;
using System.Text.RegularExpressions;

namespace dotParser
{
    class DotParser
    {
        // Method to read in a Dot file
        public static Graph DotIn(string FileInName)
        {
            Graph newGraph = new Graph();

            // Let's read in the Dot file
            FileStream file = new FileStream(FileInName, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(file);
            string strLine;

            while (sr.Peek() >= 0)
            {
                strLine = sr.ReadLine().Trim();

                #region When line is not empty
                if (strLine.Length > 1)
                {
                    // This will remember what kind of nodes we are dealing with for current line
                    NodeType nType = NodeType.NormalNode;

                    #region First line we get graph name
                    if (strLine[strLine.Length - 1].Equals('{'))
                    {
                        string[] splitData = Regex.Split(strLine, " +");
                        string strTemp = splitData[splitData.Length - 1].Trim();
                        if (strTemp.Length > 1)
                        {
                            newGraph.Name = strTemp.Remove(strTemp.Length - 1);
                        }
                        else
                        {
                            newGraph.Name = splitData[splitData.Length - 2].Trim();
                        }
                    }
                    #endregion

                    #region Lines starting with "node" tell us about query nodes and evidence nodes
                    if (strLine.Substring(0, 4).ToLower() == "node")
                    {
                        // Let's first remove "nodes " from string
                        strLine = strLine.Remove(0,5).Trim();
                        // Remove the semicolor at the end if there's one
                        if (strLine[strLine.Length - 1].Equals(';'))
                        {
                            strLine = strLine.Remove(strLine.Length - 1).Trim();
                        }

                        string[] splitData = strLine.Split(';');
                        if (splitData.Length == 2 && strLine[9].ToString().ToLower() != "c")
                        {
                            // Oval nodes representing query nodes
                            if (splitData.Length == 2 && strLine[9].ToString().ToLower() == "o")
                            {
                                nType = NodeType.QueryNode;
                            }
                            // Double circle nodes representing evidence nodes
                            if (splitData.Length == 2 && strLine[9].ToString().ToLower() == "d")
                            {
                                nType = NodeType.EvidenceNode;
                            }
                            string[] nodes = Regex.Split(splitData[1].Trim(), " +");
                            foreach (string s in nodes)
                            {
                                Node n = new Node();
                                n.Name = s;
                                n.Type = nType;
                                newGraph.Nodes.Add(s, n);
                                if (nType == NodeType.QueryNode)
                                {
                                    newGraph.QueryNodes.Add(n.Name, n);
                                }
                                if (nType == NodeType.EvidenceNode)
                                {
                                    newGraph.EvidenceNodes.Add(n.Name, n);
                                }
                            }
                        }
                    }
                    #endregion

                    #region For lines defining edges
                    if (strLine.Contains("->") || strLine.Contains("<-"))
                    {
                        string strP = "";
                        string strC = "";

                        // Remove the semicolor at the end
                        if (strLine[strLine.Length - 1].Equals(';'))
                        {
                            strLine = strLine.Remove(strLine.Length - 1);
                        }

                        // Let's first figure out who's parent and who's child
                        if (strLine.Contains("->"))
                        {
                            // Replace " -> " with comma
                            strLine = strLine.Replace(" -> ", ",");

                            string[] splitData = strLine.Split(',');
                            strP = splitData[0];
                            strC = splitData[1];

                        }
                        if (strLine.Contains("<-"))
                        {
                            // Replace " -> " with comma
                            strLine = strLine.Replace(" <- ", ",");

                            string[] splitData = strLine.Split(',');
                            strP = splitData[1];
                            strC = splitData[0];
                        }

                        // Now let's check if parent or child is already in Graph
                        Node parent;
                        newGraph.Nodes.TryGetValue(strP, out parent);
                        if (parent == null)
                        {
                            parent = new Node();
                            parent.Name = strP;
                            newGraph.Nodes.Add(strP, parent);
                            parent.Type = NodeType.NormalNode;
                        }
                        Node child;
                        newGraph.Nodes.TryGetValue(strC, out child);
                        if (child == null)
                        {
                            child = new Node();
                            child.Name = strC;
                            newGraph.Nodes.Add(strC, child);
                            child.Type = NodeType.NormalNode;
                        }
                        parent.Children.Add(child);
                        child.Parents.Add(parent);
                    }
                    #endregion
                }
                #endregion
            }
            sr.Close();
            file.Close();
            sr = null;
            file = null;

            return newGraph;
        }

        // Method to write out a Dot file
        public static void DotOut(Graph pruned, String FileOutName)
        {
            // Setup file related variables
            FileStream file = new FileStream(FileOutName, FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(file);

            // First write graph name
            sw.Write("digraph " + pruned.Name + "_pruned{\n");

            // Write out query nodes
            sw.Write("\tnode [shape = oval];");
            foreach (Node n in pruned.QueryNodes.Values)
            {
                if (pruned.Nodes.ContainsKey(n.Name))
                {
                    sw.Write(" " + n.Name);
                }
            }
            sw.Write(";\n");

            // Write out evidence nodes
            sw.Write("\tnode [shape = doublecircle];");
            foreach (Node n in pruned.EvidenceNodes.Values)
            {
                if (pruned.Nodes.ContainsKey(n.Name))
                {
                    sw.Write(" " + n.Name);
                }
            }
            sw.Write(";\n");

            // Write out normal nodes and edges
            sw.Write("\tnode [shape = circle];\n");
            foreach (Node n in pruned.Nodes.Values)
            {
                foreach (Node c in n.Children)
                {
                    if (pruned.Nodes.ContainsKey(c.Name))
                    {
                        sw.Write("\t" + n.Name + " -> " + c.Name + ";\n");
                    }
                }
            }

            // Write end of DOT file
            sw.Write("}\n");

            sw.Close();
            file.Close();
            sw = null;
            file = null;
        }
    }
}
